/*
 * SlaveRoutine.cpp
 *
 *  Created on: 31/01/2014
 *      Author: mattia
 */

#include "SlaveRoutine.h"
#include <iostream>

using namespace std;

extern int rank, size;
extern DppmSpec * dppm;

bool isGreater(Tuple * t1, int pos, int mode, int * t2);

int SlaveRoutine() {

	int act = -1, mode, maxTimePreds, n = dppm->getNumActivities(), numPreds;
	int partialCost = 0, deadline = dppm->getDeadline(), i;
	int * preds;
	bool shiftBranch;
	int bsChange = 0;
	MPI_Status status;
	int msg = 0;
	int tag = 0;
	int flag = 0;
	int buff[Tuple::tuplesIntSize];
	int range[2];
	int iter = 0;

	Tuple * bestSolution = new Tuple();

	//Incializa los parametros para calcular los indices de las tuplas
	int * indBases;
	int indLen;
	defineIndexesParams(dppm, indLen, indBases);

	//ASUMO QUE HAY ALGO PARA HACER
	MPI_Send(&msg, 1, MPI_INT, MASTER_RANK, PROCESS_DONE, MPI_COMM_WORLD);
	MPI_Recv(&msg, 1, MPI_INT, MASTER_RANK, PROCESS_DONE, MPI_COMM_WORLD, &status);
	MPI_Recv(range, 2, MPI_INT, MASTER_RANK, PROCESS_DONE, MPI_COMM_WORLD, &status);

	int * firstTupleModes = indexToModes(range[0], indLen, indBases, dppm, FILL_WITH_FIRST_MODES);
	int * lastTupleModes = indexToModes(range[1], indLen, indBases, dppm, FILL_WITH_LAST_MODES);

	Tuple * currentTuple = makeTupleFromModes(dppm, firstTupleModes);

	//Bucle principal
	do {

		//Encontrar el primer prefijo factible
		do{

			act++;
			partialCost += dppm->getActivityModeCost(act,currentTuple->modes(act));

		} while((act <= n - 2) &&
				(currentTuple->partialTimes(act) + dppm->getMinTimeToEnd(act) < deadline) &&
				(partialCost + dppm->getMinCostToEnd(act) < bestSolution->cost()));

		mode = currentTuple->modes(act);
		currentTuple->cost(partialCost - dppm->getActivityModeCost(act, mode));


		while (!isGreater(currentTuple, act, mode, lastTupleModes)) {

			iter++;

			//Predecesores la siguiente actividad
			numPreds = dppm->getActivityNumPreds(act);
			preds = dppm->getActivityPreds(act);

			//Calculo el tiempo maximo que demoran sus predecesores
			maxTimePreds = 0;
			for (int i = 0; i < numPreds; i++) {
				if (currentTuple->times(preds[i]) > maxTimePreds) {
					maxTimePreds = currentTuple->times(preds[i]);
				}
			}


			//BOUNDING
			//Me salteo todos los modos con los cuales me paso del deadline tomando los tiempos mas bajos
			while((mode < dppm->getActivityNumModes(act))
				       && (maxTimePreds + dppm->getActivityModeTime(act, mode) + dppm->getMinTimeToEnd(act) > deadline)) {

				mode++;

			};

			if ( ! (shiftBranch = (mode == dppm->getActivityNumModes(act))) ){

				if ( currentTuple->cost() + dppm->getActivityModeCost(act,mode) + dppm->getMinCostToEnd(act) >= bestSolution->cost()){

					mode = dppm->getActivityNumModes(act);
					shiftBranch = true;

				}else{
					//Setear (act,modo)
					currentTuple->modes(act, mode);
					currentTuple->times(act, maxTimePreds + dppm->getActivityModeTime(act, mode) );
					if ((act == 0) || (currentTuple->times(act) > currentTuple->partialTimes(act - 1))){
						currentTuple->partialTimes(act, currentTuple->times(act));
					}else{
						currentTuple->partialTimes(act, currentTuple->partialTimes(act - 1));
					}
					currentTuple->cost( currentTuple->cost() + dppm->getActivityModeCost(act, mode) );

					if (act < n - 1){

						mode = 0;
						act ++;

					}else{

						bsChange = evalSolution(currentTuple, bestSolution);
						currentTuple->cost( currentTuple->cost() - dppm->getActivityModeCost(act, mode++) );

						mode = bsChange ? dppm->getActivityNumModes(act) : mode + 1;

						shiftBranch = true;

					}

				}
			}

			if (shiftBranch){

				while ( (act > 0) && (mode == dppm->getActivityNumModes(act)) ){

					act--;

					currentTuple->cost( currentTuple->cost() -
							dppm->getActivityModeCost(act, currentTuple->modes(act)) );

					mode = currentTuple->modes(act) + 1;

				}

			}

			if (bsChange) {
				bsChange = false;
				MPI_Send(&msg, 1, MPI_INT, MASTER_RANK, BEST_SOLUTION_UPDATE, MPI_COMM_WORLD);
				MPI_Send(bestSolution->getArray(), Tuple::tuplesIntSize, MPI_INT, MASTER_RANK, BEST_SOLUTION_UPDATE, MPI_COMM_WORLD);
			}

			MPI_Iprobe(MASTER_RANK, BEST_SOLUTION_UPDATE, MPI_COMM_WORLD, &flag, &status);

			if (flag) {
				MPI_Recv(&msg, 1, MPI_INT, status.MPI_SOURCE, status.MPI_TAG, MPI_COMM_WORLD, &status);
				MPI_Recv(buff, Tuple::tuplesIntSize, MPI_INT, status.MPI_SOURCE, status.MPI_TAG, MPI_COMM_WORLD, &status);
				evalSolution(buff, bestSolution);
			}
		}

		MPI_Send(&msg, 1, MPI_INT, MASTER_RANK, PROCESS_DONE, MPI_COMM_WORLD);
		MPI_Recv(&msg, 1, MPI_INT, MASTER_RANK, PROCESS_DONE, MPI_COMM_WORLD, &status);

		if (msg == NEW_RANGE) {

			MPI_Recv(range, 2, MPI_INT, MASTER_RANK, PROCESS_DONE, MPI_COMM_WORLD, &status);
			delete[] firstTupleModes;
			delete[] lastTupleModes;

			firstTupleModes = indexToModes(range[0], indLen, indBases, dppm, FILL_WITH_FIRST_MODES);
			lastTupleModes = indexToModes(range[1], indLen, indBases, dppm, FILL_WITH_LAST_MODES);

			delete currentTuple;

			currentTuple = makeTupleFromModes(dppm, firstTupleModes);

		}

	} while ((msg != FREE_TO_GO));

	delete currentTuple;
	delete bestSolution;
	delete[] firstTupleModes;
	delete[] lastTupleModes;
	delete[] indBases;

	return iter;

}


bool isGreater(Tuple * t1, int pos, int mode, int * t2) {

	for (int i = 0; i < pos; i++) {

		if (t1->modes(i) != t2[i]) {

			return t1->modes(i) > t2[i];

		}
	}

	return mode > t2[pos];
}

